<?php
class AppUpmpService{
	public $merId      = '';//商户号
	public $cert_pwd   = '';//证书密码
	public $use_type   = 'web';//使用类型
	public $notify_url = '';//异步URL
	public $return_url = '';//同步URL
	public $is_debug   = false;//true为调试模式，价格会设置成0.01，false为非调试模式，价格按正常来
	
	public function __construct($use_type='web', $merId = '', $cert_path = '', $cert_pwd = '') {
		//日志保存目录
		$this->logs_path = dirname(__FILE__).'/Logs/';
		if(file_exists($this->logs_path)==false){
			mkdir($this->logs_path,0700);
		}
		//测试环境配置
		if($this->is_debug == true){
			$this->merId = '777290058111579';
			$cert_pwd    = '000000';
			$cert_path   = dirname(__FILE__) . '/config/PM_' . $this->merId . '_acp.pfx';
			include_once dirname(__FILE__) . '/config/config_test.php';
		}
		//正式环境配置
		else{
			$this->merId = $merId;
			// var_dump($use_type);
			// var_dump($this->merId);
			// var_dump($cert_pwd);
			// exit;
			$cert_path = $cert_path ? $cert_path : dirname(__FILE__) . '/config/PM_' . $merId . '_acp.pfx';
			include_once dirname(__FILE__) . '/config/config.php';
		}
		//签名证书路径
		define('SDK_SIGN_CERT_PATH', $cert_path);
		//签名证书密码
		define('SDK_SIGN_CERT_PWD', $cert_pwd);
		// 密码加密证书（这条用不到的请随便配）
		define('SDK_ENCRYPT_CERT_PATH',dirname(__FILE__) . '/config/verify_sign_acp.cer');
		// 验签证书路径（请配到文件夹，不要配到具体文件）
		define('SDK_VERIFY_CERT_DIR',dirname(__FILE__) . '/config/');
		//文件下载目录 
		define('SDK_FILE_DOWN_PATH',dirname(__FILE__) . '/downfile/');
		//日志 目录 
		define('SDK_LOG_FILE_PATH',dirname(__FILE__) . '/Logs/');

    }

    public function get_pay_sign($order_sn, $title, $amount){
    	$params = array(
			'version'      => '5.0.0',//版本号
			'encoding'     => 'utf-8',//编码方式
			'certId'       => $this->getSignCertId(),//证书ID
			'txnType'      => '01',//交易类型	
			'txnSubType'   => '01',//交易子类
			'bizType'      => '000201',//业务类型
			'frontUrl'     => $this->return_url ? $this->return_url : $this->notify_url,//前台通知地址，控件接入的时候不会起作用
			'backUrl'      => $this->notify_url,//后台通知地址	
			'signMethod'   => '01',//签名方法
			'channelType'  => '08',//渠道类型，07-PC，08-手机
			'accessType'   => '0',//接入类型
			'merId'        => $this->merId,//商户代码，请改自己的测试商户号
			'orderId'      => $order_sn,//商户订单号，8-40位数字字母
			'txnTime'      => date('YmdHis'),//订单发送时间
			'txnAmt'       => $amount * 100,//交易金额，单位分
			'currencyCode' => '156',//交易币种
			// 'orderDesc'    => '',//订单描述，可不上送，上送时控件中会显示该信息
			// 'reqReserved'  => '',//请求方保留域，透传字段，查询、通知、对账文件中均会原样出现
		);
		// 签名
		$this->sign($params);
		// 发送信息到后台
		$result = $this->sendHttpRequest($params, SDK_App_Request_Url);

		//返回结果展示
		$result_arr = $this->coverStringToArray($result);
		$result_arr['mode'] = $this->is_debug ? '01' : '00';
		// $this->deal_file($result_arr);
		return $result_arr['respCode'] == '00' ? $result_arr : false;
    }
    //消费撤销
    public function consume_undo($order_sn,$query_id='',$amount=''){
    	$params = array(
			'version'     => '5.0.0',//版本号
			'encoding'    => 'utf-8',//编码方式
			'certId'      => $this->getSignCertId(),//证书ID	
			'signMethod'  => '01',//签名方法
			'txnType'     => '31',//交易类型	
			'txnSubType'  => '00',//交易子类
			'bizType'     => '000201',//业务类型
			'accessType'  => '0',//接入类型
			'channelType' => '07',//渠道类型
			'orderId'     => $order_sn,//商户订单号，重新产生，不同于原消费
			'merId'       => $this->merId,//商户代码，请改成自己的测试商户号
			'origQryId'   => $query_id,    //原消费的queryId，可以从查询接口或者通知接口中获取
			'txnTime'     => date('YmdHis'),//订单发送时间，重新产生，不同于原消费
			'txnAmt'      => $amount * 100,//交易金额，消费撤销时需和原消费一致
			'backUrl'     => $this->notify_url,//后台通知地址
			// 'reqReserved' =>' 透传信息',//请求方保留域，透传字段，查询、通知、对账文件中均会原样出现
		);
		var_dump($params);
		// 签名
		$this->sign($params);
		// 发送信息到后台
		$result = $this->sendHttpRequest($params, SDK_BACK_TRANS_URL);
		//返回结果展示
		$result_arr = $this->coverStringToArray($result);
		var_dump($result_arr);
		return $result_arr['respCode'] == '00' ? $result_arr : false;
    }
    //WEB银联支付签名跳转到银联支付页面
    public function get_web_pay_sign($order_sn,$title='',$amount=''){
    	$params = array(
			'version'        => '5.0.0',//版本号
			'encoding'       => 'utf-8',//编码方式
			'certId'         => $this->getSignCertId(),//证书ID
			'txnType'        => '01',//交易类型	
			'txnSubType'     => '01',//交易子类
			'bizType'        => '000201',//业务类型
			'frontUrl'       => $this->return_url ? $this->return_url : $this->notify_url,//前台通知地址
			'backUrl'        => $this->notify_url,//后台通知地址	
			'signMethod'     => '01',//签名方法
			'channelType'    => '07',//渠道类型，07-PC，08-手机
			'accessType'     => '0',//接入类型
			'merId'          => $this->merId,//商户代码，请改自己的测试商户号
			'orderId'        => $order_sn,	//商户订单号
			'txnTime'        => date('YmdHis'),	//订单发送时间
			'txnAmt'         => $amount * 100,//交易金额，单位分
			'currencyCode'   => '156',//交易币种
			'defaultPayType' => '0001',//默认支付方式	
			// 'orderDesc'      => $title, //订单描述，网关支付和wap支付暂时不起作用
			// 'reqReserved'    =>' 透传信息', //请求方保留域，透传字段，查询、通知、对账文件中均会原样出现
		);
		// var_dump($params);exit;
		//签名
		$this->sign($params);
		//构造 自动提交的表单
		$html_form = $this->create_html($params, SDK_FRONT_TRANS_URL );
		exit($html_form);
    }
    /**
	 * 后台交易 HttpClient通信
	 * @param unknown_type $params
	 * @param unknown_type $url
	 * @return mixed
	 */
	public function sendHttpRequest($params, $url) {
		$opts = $this->getRequestParamString ( $params );
		
		$ch = curl_init ();
		curl_setopt ( $ch, CURLOPT_URL, $url );
		curl_setopt ( $ch, CURLOPT_POST, 1 );
		curl_setopt ( $ch, CURLOPT_SSL_VERIFYPEER, false);//不验证证书
	    curl_setopt ( $ch, CURLOPT_SSL_VERIFYHOST, false);//不验证HOST
		curl_setopt ( $ch, CURLOPT_SSLVERSION, 3);
		curl_setopt ( $ch, CURLOPT_HTTPHEADER, array (
				'Content-type:application/x-www-form-urlencoded;charset=UTF-8' 
		) );
		curl_setopt ( $ch, CURLOPT_POSTFIELDS, $opts );
		
		/**
		 * 设置cURL 参数，要求结果保存到字符串中还是输出到屏幕上。
		 */
		curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
		
		// 运行cURL，请求网页
		$html = curl_exec ( $ch );
		// close cURL resource, and free up system resources
		curl_close ( $ch );
		return $html;
	}

	/**
	 * 组装报文
	 *
	 * @param unknown_type $params        	
	 * @return string
	 */
	public function getRequestParamString($params) {
		$params_str = '';
		foreach ( $params as $key => $value ) {
			$params_str .= ($key . '=' . (!isset ( $value ) ? '' : urlencode( $value )) . '&');
		}
		return substr ( $params_str, 0, strlen ( $params_str ) - 1 );
	}

    /**
	 * 数组 排序后转化为字体串
	 *
	 * @param array $params        	
	 * @return string
	 */
	public function coverParamsToString($params) {
		$sign_str = '';
		// 排序
		ksort ( $params );
		foreach ( $params as $key => $val ) {
			if ($key == 'signature') {
				continue;
			}
			$sign_str .= sprintf ( "%s=%s&", $key, $val );
			// $sign_str .= $key . '=' . $val . '&';
		}
		return substr ( $sign_str, 0, strlen ( $sign_str ) - 1 );
	}
	/**
	 * 字符串转换为 数组
	 *
	 * @param unknown_type $str        	
	 * @return multitype:unknown
	 */
	public function coverStringToArray($str) {
		$result = array ();

		if (! empty ( $str )) {
			$temp = preg_split ( '/&/', $str );
			if (! empty ( $temp )) {
				foreach ( $temp as $key => $val ) {
					$arr = preg_split ( '/=/', $val, 2 );
					if (! empty ( $arr )) {
						$k = $arr ['0'];
						$v = $arr ['1'];
						$result [$k] = $v;
					}
				}
			}
		}
		return $result;
	}
	/**
	 * 处理返回报文 解码客户信息 , 如果编码为utf-8 则转为utf-8
	 *
	 * @param unknown_type $params        	
	 */
	public function deal_params(&$params) {
		/**
		 * 解码 customerInfo
		 */
		if (! empty ( $params ['customerInfo'] )) {
			$params ['customerInfo'] = base64_decode ( $params ['customerInfo'] );
		}
		
		if (! empty ( $params ['encoding'] ) && strtoupper ( $params ['encoding'] ) == 'utf-8') {
			foreach ( $params as $key => $val ) {
				$params [$key] = iconv ( 'utf-8', 'UTF-8', $val );
			}
		}
	}

	/**
	 * 压缩文件 对应java deflate
	 *
	 * @param unknown_type $params        	
	 */
	public function deflate_file(&$params) {
		foreach ( $_FILES as $file ) {
			if (file_exists ( $file ['tmp_name'] )) {
				$params ['fileName'] = $file ['name'];
				
				$file_content = file_get_contents ( $file ['tmp_name'] );
				$file_content_deflate = gzcompress ( $file_content );
				
				$params ['fileContent'] = base64_encode ( $file_content_deflate );
			} else {
				//文件上传失败
			}
		}
	}

	/**
	 * 处理报文中的文件
	 *
	 * @param unknown_type $params        	
	 */
	public function deal_file($params) {
		if (isset ( $params ['fileContent'] )) {
			$fileContent = $params ['fileContent'];
			
			if (empty ( $fileContent )) {
				exit('文件内容为空');
			} else {
				// 文件内容 解压缩
				$content = gzuncompress ( base64_decode ( $fileContent ) );
				$root = SDK_FILE_DOWN_PATH;
				$filePath = null;
				if (empty ( $params ['fileName'] )) {
					$filePath = $root . $params ['merId'] . '_' . $params ['batchNo'] . '_' . $params ['txnTime'] . '.txt';
				} else {
					$filePath = $root . $params ['fileName'];
				}
				$handle = fopen ( $filePath, "w+" );
				if (! is_writable ( $filePath )) {
					exit ( "文件:" . $filePath . "不可写，请检查！" );
				} else {
					file_put_contents ( $filePath, $content );
				}
				fclose ( $handle );
			}
		}
	}

	/**
	 * 构造自动提交表单
	 *
	 * @param unknown_type $params        	
	 * @param unknown_type $action        	
	 * @return string
	 */
	public function create_html($params, $action) {
		$encodeType = isset ( $params ['encoding'] ) ? $params ['encoding'] : 'UTF-8';
		$html = '<html>
		<head>
		    <meta http-equiv="Content-Type" content="text/html; charset=' . $encodeType . '" />
		</head>
		<body  onload="javascript:document.pay_form.submit();">
		    <form id="pay_form" name="pay_form" action="' . $action . '" method="post">';
			foreach ( $params as $key => $value ) {
				$html .= "    <input type=\"hidden\" name=\"{$key}\" id=\"{$key}\" value=\"{$value}\" />\n";
			}
			$html .= '<input type="submit" value="正在跳转到银联支付，请稍候...">
		    </form>
		</body>
		</html>';
		return $html;
	}
	/**
	 * Author: gu_yongkang 
	 * data: 20110510
	 * 密码转PIN 
	 * Enter description here ...
	 * @param $spin
	 */
	public function  Pin2PinBlock( &$sPin ){
		$iTemp = 1;
		$sPinLen = strlen($sPin);
		$sBuf = array();
		//密码域大于10位
		$sBuf[0]=intval($sPinLen, 10);
	
		if($sPinLen % 2 ==0){
			for ($i=0; $i<$sPinLen;){
				$tBuf = substr($sPin, $i, 2);
				$sBuf[$iTemp] = intval($tBuf, 16);
				unset($tBuf);
				if ($i == ($sPinLen - 2)){
					if ($iTemp < 7){
						$t = 0;
						for ($t=($iTemp+1); $t<8; $t++){
							$sBuf[$t] = 0xff;
						}
					}
				}
				$iTemp++;
				$i = $i + 2;	//linshi
			}
		}else{
			for ($i=0; $i<$sPinLen;){
				if ($i ==($sPinLen-1)){
					$mBuf = substr($sPin, $i, 1) . "f";
					$sBuf[$iTemp] = intval($mBuf, 16);
					unset($mBuf);
					if (($iTemp)<7){
						$t = 0;
						for ($t=($iTemp+1); $t<8; $t++){
							$sBuf[$t] = 0xff;
						}
					}
				}else{
					$tBuf = substr($sPin, $i, 2);
					$sBuf[$iTemp] = intval($tBuf, 16);
					unset($tBuf);
				}
				$iTemp++;
				$i = $i + 2;
			}
		}
		return $sBuf;
	}
	/**
	 * Author: gu_yongkang
	 * data: 20110510
	 * Enter description here ...
	 * @param $sPan
	 */
	public function FormatPan(&$sPan){
		$iPanLen = strlen($sPan);
		$iTemp   = $iPanLen - 13;
		$sBuf    = array();
		$sBuf[0] = 0x00;
		$sBuf[1] = 0x00;
		for ($i=2; $i<8; $i++){
			$tBuf     = substr($sPan, $iTemp, 2);
			$sBuf[$i] = intval($tBuf, 16);
			$iTemp    = $iTemp + 2;		
		}
		return $sBuf;
	}
	
	public function Pin2PinBlockWithCardNO(&$sPin, &$sCardNO){
		$sPinBuf  = $this->Pin2PinBlock($sPin);
		$iCardLen = strlen($sCardNO);
		if ($iCardLen <= 10){
			return (1);
		}
		elseif ($iCardLen==11){
			$sCardNO = "00" . $sCardNO;
		}
		elseif ($iCardLen==12){
			$sCardNO = "0" . $sCardNO;
		}
		$sPanBuf = $this->FormatPan($sCardNO);
		$sBuf = array();
		
		for ($i=0; $i<8; $i++){
			$sBuf[$i] = vsprintf("%c", ($sPinBuf[$i] ^ $sPanBuf[$i]));
		}
		unset($sPinBuf);
		unset($sPanBuf);
		$sOutput = implode("", $sBuf);	//数组转换为字符串
		return $sOutput;
	}


	public function EncryptedPin($sPin, $sCardNo ,$sPubKeyURL){
		$sPubKeyURL = trim(SDK_ENCRYPT_CERT_PATH," ");
		$fp = fopen($sPubKeyURL, "r");
		if ($fp != NULL){
			$sCrt = fread($fp, 8192);
			fclose($fp);
		}
		$sPubCrt = openssl_x509_read($sCrt);
		if ($sPubCrt === FALSE){
			print("openssl_x509_read in false!");
			return (-1);
		}
		$sPubKey = openssl_x509_parse($sPubCrt);
	
		$sInput = Pin2PinBlockWithCardNO($sPin, $sCardNo);
		if ($sInput == 1){
			print("Pin2PinBlockWithCardNO Error ! : " . $sInput);
			return (1);
		}
		$iRet = openssl_public_encrypt($sInput, $sOutData, $sCrt, OPENSSL_PKCS1_PADDING);
		if ($iRet === TRUE){
			$sBase64EncodeOutData = base64_encode($sOutData);		
			return $sBase64EncodeOutData;
		}
		else{
			print("openssl_public_encrypt Error !");
			return (-1);
		}
	}
	/**
	 * 签名
	 *
	 * @param String $params_str
	 */
	public function sign(&$params) {
		if(isset($params['transTempUrl'])){
			unset($params['transTempUrl']);
		}
		// 转换成key=val&串
		$params_str     = $this->coverParamsToString ( $params );
		$params_sha1x16 = sha1 ( $params_str, FALSE );
		// 签名证书路径
		$cert_path   = SDK_SIGN_CERT_PATH;
		$private_key = $this->getPrivateKey ( $cert_path );
		// 签名
		$sign_falg = openssl_sign ( $params_sha1x16, $signature, $private_key, OPENSSL_ALGO_SHA1 );
		if ($sign_falg) {
			$signature_base64 = base64_encode ( $signature );
			$params ['signature'] = $signature_base64;
		} else {
			exit('签名失败');
		}
	}

	/**
	 * 验签
	 *
	 * @param String $params_str        	
	 * @param String $signature_str        	
	 */
	public function verify($params) {
		// 公钥
		$public_key = $this->getPulbicKeyByCertId ( $params ['certId'] );	
		// 签名串
		$signature_str = $params ['signature'];
		unset ( $params ['signature'] );
		//报文去[signature] key=val&串
		$params_str     = $this->coverParamsToString ( $params );
		$signature      = base64_decode ( $signature_str );
		$params_sha1x16 = sha1 ( $params_str, FALSE );
		$isSuccess      = openssl_verify ( $params_sha1x16, $signature,$public_key, OPENSSL_ALGO_SHA1 );
		return $isSuccess;
	}

	/**
	 * 根据证书ID 加载 证书
	 *
	 * @param unknown_type $certId        	
	 * @return string NULL
	 */
	function getPulbicKeyByCertId($certId) {
		// 证书目录
		$cert_dir = SDK_VERIFY_CERT_DIR;
		$handle   = opendir ( $cert_dir );
		if ($handle) {
			while ( $file = readdir ( $handle ) ) {
				clearstatcache ();
				$filePath = $cert_dir . '/' . $file;
				if (is_file ( $filePath )) {
					if (pathinfo ( $file, PATHINFO_EXTENSION ) == 'cer') {
						if ($this->getCertIdByCerPath ( $filePath ) == $certId) {
							closedir ( $handle );
							return $this->getPublicKey ( $filePath );
						}
					}
				}
			}
			exit('没有找到证书ID为[' . $certId . ']的证书');
		} else {
			exit( '证书目录 ' . $cert_dir . '不正确' );
		}
		closedir ( $handle );
		return null;
	}

	/**
	 * 取证书ID(.pfx)
	 *
	 * @return unknown
	 */
	public function getCertId($cert_path) {
		$pkcs12certdata = file_get_contents ( $cert_path );

		openssl_pkcs12_read ( $pkcs12certdata, $certs, SDK_SIGN_CERT_PWD );
		$x509data = $certs ['cert'];
		openssl_x509_read ( $x509data );
		$certdata = openssl_x509_parse ( $x509data );
		$cert_id = $certdata ['serialNumber'];
		return $cert_id;
	}

	/**
	 * 取证书ID(.cer)
	 *
	 * @param unknown_type $cert_path        	
	 */
	public function getCertIdByCerPath($cert_path) {
		$x509data = file_get_contents ( $cert_path );
		openssl_x509_read ( $x509data );
		$certdata = openssl_x509_parse ( $x509data );
		$cert_id = $certdata ['serialNumber'];
		return $cert_id;
	}

	/**
	 * 签名证书ID
	 *
	 * @return unknown
	 */
	public function getSignCertId() {
		// 签名证书路径
		
		return $this->getCertId ( SDK_SIGN_CERT_PATH );
	}
	public function getEncryptCertId() {
		// 签名证书路径
		return $this->getCertIdByCerPath ( SDK_ENCRYPT_CERT_PATH );
	}

	/**
	 * 取证书公钥 -验签
	 *
	 * @return string
	 */
	public function getPublicKey($cert_path) {
		return file_get_contents ( $cert_path );
	}
	/**
	 * 返回(签名)证书私钥 -
	 *
	 * @return unknown
	 */
	public function getPrivateKey($cert_path) {
		$pkcs12 = file_get_contents ( $cert_path );
		openssl_pkcs12_read ( $pkcs12, $certs, SDK_SIGN_CERT_PWD );
		return $certs ['pkey'];
	}

	/**
	 * 加密 卡号
	 *
	 * @param String $pan
	 *        	卡号
	 * @return String
	 */
	public function encryptPan($pan) {
		$cert_path  = MPI_ENCRYPT_CERT_PATH;
		$public_key = $this->getPublicKey ( $cert_path );
		
		openssl_public_encrypt ( $pan, $cryptPan, $public_key );
		return base64_encode ( $cryptPan );
	}
	/**
	 * pin 加密
	 *
	 * @param unknown_type $pan        	
	 * @param unknown_type $pwd        	
	 * @return Ambigous <number, string>
	 */
	public function encryptPin($pan, $pwd) {
		$cert_path  = SDK_ENCRYPT_CERT_PATH;
		$public_key = $this->getPublicKey ( $cert_path );

		return $this->EncryptedPin ( $pwd, $pan, $public_key );
	}
	/**
	 * cvn2 加密
	 *
	 * @param unknown_type $cvn2        	
	 * @return unknown
	 */
	public function encryptCvn2($cvn2) {
		$cert_path  = SDK_ENCRYPT_CERT_PATH;
		$public_key = $this->getPublicKey ( $cert_path );
		
		openssl_public_encrypt ( $cvn2, $crypted, $public_key );
		
		return base64_encode ( $crypted );
	}
	/**
	 * 加密 有效期
	 *
	 * @param unknown_type $certDate        	
	 * @return unknown
	 */
	public function encryptDate($certDate) {
		$cert_path  = SDK_ENCRYPT_CERT_PATH;
		$public_key = $this->getPublicKey ( $cert_path );
		
		openssl_public_encrypt ( $certDate, $crypted, $public_key );
		
		return base64_encode ( $crypted );
	}

	/**
	 * 加密 数据
	 *
	 * @param unknown_type $certDatatype
	 * @return unknown
	 */
	public function encryptDateType($certDataType) {
		$cert_path  = SDK_ENCRYPT_CERT_PATH;
		$public_key = $this->getPublicKey ( $cert_path );

		openssl_public_encrypt ( $certDataType, $crypted, $public_key );

		return base64_encode ( $crypted );
	}
}
?>